#include "imdPrecompile.h"
#include "imdPlayer.h"
#include "imtSceneEntity.h"
#include "imtSceneLight.h"
#include "imtAttribSet.h"

namespace iMate {

	class Attrib_ISObj : public IAttrib
	{
	public:
		Attrib_ISObj(const String& name, ISceneObject* obj) : IAttrib(name, obj)
		{}
		virtual bool readOnly() const 
		{
			if (mName == "priority")
				return false;
			return true;
		}	
		virtual bool set(const String& strValue)
		{
			if (mName == "priority")
			{
				mObject->setPriority(Ogre::StringConverter::parseUnsignedInt(strValue));
				return true;
			}
			return false;
		}
		virtual String get() const 
		{
			if (mName == "priority")
			{
				return Ogre::StringConverter::toString(mObject->getPriority());
			}
			if (mName == "type")
			{
				return mObject->getTypeName();
			}
			if (mName == "name")
			{
				return mObject->getName();
			}
			return gNullName;
		}
	};
	class Attrib_VSObj : public IAttrib
	{
	public:
		Attrib_VSObj(const String& name, ISceneObject* obj) : IAttrib(name, obj)
		{}
		virtual bool readOnly() const 
		{
			if (mName == "enable")
				return false;
			return true;
		}	
		virtual bool set(const String& strValue)
		{
			assert(mObject);

			if (mObject->is_a<VirtualSceneObject>())
			{
				VirtualSceneObject* obj = (VirtualSceneObject*)mObject;
				if (mName == "enable")
				{
					obj->setEnabled(Ogre::StringConverter::parseBool(strValue));
					return true;
				}
			}

			return false;
		}
		virtual String get() const 
		{
			assert(mObject);

			if (mObject->is_a<VirtualSceneObject>())
			{
				VirtualSceneObject* obj = (VirtualSceneObject*)mObject;
				if (mName == "enable")
				{
					return Ogre::StringConverter::toString(obj->isEnabled());
				}
			}

			return gNullName;
		}
	};
	class Attrib_RSObj : public IAttrib
	{
	public:
		Attrib_RSObj(const String& name, ISceneObject* obj) : IAttrib(name, obj)
		{}
		virtual bool readOnly() const 
		{
			return false;
		}	
		virtual bool set(const String& strValue)
		{
			assert(mObject);

			if (mObject->is_a<RealSceneObject>())
			{
				RealSceneObject* obj = (RealSceneObject*)mObject;
				if (mName == "visible")
				{
					obj->setVisible(Ogre::StringConverter::parseBool(strValue));
					return true;
				}
				else if (mName == "position")
				{
					obj->setPosition(Ogre::StringConverter::parseVector3(strValue));
					return true;
				}
				else if (mName == "orientation")
				{
					obj->setOrientation(Ogre::StringConverter::parseQuaternion(strValue));
					return true;
				}
				else if (mName == "scale")
				{
					obj->setScale(Ogre::StringConverter::parseVector3(strValue));
					return true;
				}
				else if (mName == "queryFlag")
				{
					obj->setQueryFlags(Ogre::StringConverter::parseUnsignedInt(strValue));
					return true;
				}
			}

			return false;
		}
		virtual String get() const 
		{
			assert(mObject);

			if (mObject->is_a<RealSceneObject>())
			{
				RealSceneObject* obj = (RealSceneObject*)mObject;
				if (mName == "visible")
				{
					return Ogre::StringConverter::toString(obj->getDummy()->getAttachedObject(0)->isVisible());
				}
				else if (mName == "position")
				{
					return Ogre::StringConverter::toString(obj->getDummy()->getPosition());
				}
				else if (mName == "orientation")
				{
					return Ogre::StringConverter::toString(obj->getDummy()->getOrientation());
				}
				else if (mName == "scale")
				{
					return Ogre::StringConverter::toString(obj->getDummy()->getScale());
				}
				else if (mName == "queryFlag")
				{
					return Ogre::StringConverter::toString(obj->getDummy()->getAttachedObject(0)->getQueryFlags());
				}
			}

			return gNullName;
		}
	};
	class Attrib_ESObj : public IAttrib
	{
	public:
		Attrib_ESObj(const String& name, ISceneObject* obj) : IAttrib(name, obj)
		{}
		virtual bool readOnly() const 
		{
			return true;
		}	
		virtual bool set(const String& strValue)
		{
			assert(mObject);
			return false;
		}
		virtual String get() const 
		{
			assert(mObject);

			if (mObject->is_a<SceneEntity>())
			{
				SceneEntity* obj = (SceneEntity*)mObject;
				if (mName == "mesh")
				{
					return ((Ogre::Entity*)obj->getDummy()->getAttachedObject(0))->getMesh()->getName();
				}
				else if (mName == "material")
				{
					return ((Ogre::Entity*)obj->getDummy()->getAttachedObject(0))->getSubEntity(0)->getMaterialName();
				}
				else if (mName == "skeleton")
				{
					Ogre::SkeletonInstance *instance = ((Ogre::Entity*)obj->getDummy()->getAttachedObject(0))->getSkeleton();
					if (instance)
						return instance->getName();
				}
			}

			return gNullName;
		}
	};
	class Attrib_LSObj : public IAttrib
	{
	public:
		Attrib_LSObj(const String& name, ISceneObject* obj) : IAttrib(name, obj)
		{}
		virtual bool readOnly() const 
		{
			return false;
		}	
		virtual bool set(const String& strValue)
		{
			assert(mObject);
			if (mObject->is_a<SceneLight>())
			{
				SceneLight* obj = (SceneLight*)mObject;
				if (mName == "diffuse")
				{
					obj->getLight()->setDiffuseColour(
						Ogre::StringConverter::parseColourValue(strValue));
					return true;
				}
				if (mName == "specular")
				{
					obj->getLight()->setSpecularColour(
						Ogre::StringConverter::parseColourValue(strValue));
					return true;
				}
			}
			return false;
		}
		virtual String get() const 
		{
			assert(mObject);

			if (mObject->is_a<SceneLight>())
			{
				SceneLight* obj = (SceneLight*)mObject;
				if (mName == "diffuse")
				{
					return Ogre::StringConverter::toString(obj->getLight()->getDiffuseColour());
				}
				if (mName == "specular")
				{
					return Ogre::StringConverter::toString(obj->getLight()->getSpecularColour());
				}
			}

			return gNullName;
		}
	};

	void AttribSet::getAttribs(std::vector<IAttrib*>& attribs)
	{
		attribs.insert(attribs.end(), mAttribs.begin(), mAttribs.end());
	}
	IAttrib* AttribSet::getAttrib(const String& attribName)
	{
		IAttrib* attrib = findAttribInSelf(attribName);
		if (!attrib && mMoreAttribSet)
		{
			attrib = mMoreAttribSet->getAttrib(attribName);
		}
		return attrib;
	}
	void AttribSet::addAttrib(IAttrib* attrib)
	{
		if (attrib)
		{
			mAttribs.push_back(attrib);
		}
	}
	void AttribSet::destroy()
	{
		for (size_t i=0; i<mAttribs.size(); ++i)
		{
			delete mAttribs[i];
		}
		mAttribs.clear();
	}
	IAttrib* AttribSet::findAttribInSelf(const String& attribName)
	{
		for (size_t i=0; i<mAttribs.size(); ++i)
		{
			if (mAttribs[i]->getName() == attribName)
				return mAttribs[i];
		}
		return 0;
	}

#define count_of(array) (sizeof(array) == 0 ? 0 : sizeof(array) / sizeof(array[0]))

	AttribSet* imtCreateAttribSet(ISceneObject* obj)
	{
		const char* Attrib_ISObj_Strs[] = 
		{
			"type", 
			"name", 
			"priority"
		};
		const char* Attrib_VSObj_Strs[] = 
		{
			"enable"
		};
		const char* Attrib_RSObj_Strs[] = 
		{
			"visible",
			"position",
			"orientation",
			"scale",
			"queryFlag"
		};
		const char* Attrib_ESObj_Strs[] = 
		{
			"mesh",
			"material",
			"skeleton"
		};
		const char* Attrib_LSObj_Strs[] = 
		{
			"diffuse",
			"specular"
		};

		assert(obj);
		AttribSet* attribSet = new AttribSet(obj);
		if (obj->is_a<ISceneObject>())
		{
			for (size_t i=0; i<count_of(Attrib_ISObj_Strs); ++i)
			{
				attribSet->addAttrib(new Attrib_ISObj(Attrib_ISObj_Strs[i], obj));
			}
		}
		if (obj->is_a<VirtualSceneObject>())
		{
			for (size_t i=0; i<count_of(Attrib_VSObj_Strs); ++i)
			{
				attribSet->addAttrib(new Attrib_VSObj(Attrib_VSObj_Strs[i], obj));
			}
		}
		if (obj->is_a<RealSceneObject>())
		{
			for (size_t i=0; i<count_of(Attrib_RSObj_Strs); ++i)
			{
				attribSet->addAttrib(new Attrib_RSObj(Attrib_RSObj_Strs[i], obj));
			}
		}
		if (obj->is_a<SceneEntity>())
		{
			for (size_t i=0; i<count_of(Attrib_ESObj_Strs); ++i)
			{
				attribSet->addAttrib(new Attrib_ESObj(Attrib_ESObj_Strs[i], obj));
			}
		}
		if (obj->is_a<SceneLight>())
		{
			for (size_t i=0; i<count_of(Attrib_LSObj_Strs); ++i)
			{
				attribSet->addAttrib(new Attrib_LSObj(Attrib_LSObj_Strs[i], obj));
			}
		}
		return attribSet;
	}

	//============================================
	
	class AttribSheet : public NormalAlloc
	{
	protected:
		IAttrib* mAttrib;
		CEGUI::Window* mParent;
		CEGUI::Window* mLabel;
		CEGUI::Window* mValueWindow;
	public:
		AttribSheet(IAttrib* attrib, CEGUI::Window* parent) 
			: mAttrib(attrib), mParent(parent), 
			mLabel(0), mValueWindow(0)
		{}
		virtual ~AttribSheet()
		{
			destroy();
		}
		virtual void setGeomentry(const CEGUI::UVector2& pos, const CEGUI::UVector2& size)
		{
			assert(mAttrib && mParent && mLabel && mValueWindow);

			mLabel->setPosition(pos);
			CEGUI::UDim width(size.d_x.d_scale / 2.5f, size.d_x.d_offset / 2.5f);
			mLabel->setSize(CEGUI::UVector2(width, size.d_y));
			mValueWindow->setPosition(CEGUI::UVector2(pos.d_x + width, pos.d_y));
			mValueWindow->setSize(CEGUI::UVector2(size.d_x - width, size.d_y));
		}
		virtual void destroy()
		{
			if (mAttrib && mParent && mLabel && mValueWindow)
			{
				CEGUI::WindowManager* wm = CEGUI::WindowManager::getSingletonPtr();
				mParent->removeChildWindow(mLabel);
				mParent->removeChildWindow(mValueWindow);
				wm->destroyWindow(mLabel);
				wm->destroyWindow(mValueWindow);

				mParent = 0;
				mLabel = 0;
				mValueWindow = 0;
			}
		}
		virtual void create()
		{
			destroy();
			if (mAttrib)
			{
				CEGUI::WindowManager* wm = CEGUI::WindowManager::getSingletonPtr();

				mLabel = wm->createWindow("TaharezLook/StaticText");
				mLabel->setDestroyedByParent(false);
				mLabel->setText(mAttrib->getName().c_str());
				mParent->addChildWindow(mLabel);
				mLabel->setVisible(true);

				mValueWindow = wm->createWindow("TaharezLook/Editbox");
				((CEGUI::Editbox*)mValueWindow)->setReadOnly(mAttrib->readOnly());
				mValueWindow->setDestroyedByParent(false);
				mValueWindow->setText(mAttrib->get().c_str());
				mValueWindow->subscribeEvent(CEGUI::Editbox::EventTextAccepted,
					CEGUI::Event::Subscriber(&AttribSheet::_handleAttribEditbox, this));
				mValueWindow->subscribeEvent(CEGUI::Editbox::EventDeactivated,
					CEGUI::Event::Subscriber(&AttribSheet::_handleAttribEditbox, this));
				mParent->addChildWindow(mValueWindow);
				mValueWindow->setVisible(true);
			}
		}
		virtual void updateUi2Attrib()
		{
			assert(mAttrib && mParent && mLabel && mValueWindow);

			if (mAttrib->readOnly())
				return;

			String text = String(mValueWindow->getText().c_str());
			if (mAttrib->get() != text)
			{
				mAttrib->set(text);
			}
		}
		virtual void updateAttrib2Ui()
		{
			assert(mAttrib && mParent && mLabel && mValueWindow);

			String text = String(mValueWindow->getText().c_str());
			String value = mAttrib->get();
			if (text != value)
			{
				mValueWindow->setText(value.c_str());
			}
		}

	public:
		bool _handleAttribEditbox(const CEGUI::EventArgs& args)
		{
			updateUi2Attrib();
			return true;
		}
	};

	void AttribSetShower::rebuildAttribSheets()
	{
		clearUi();
		if (mAttribSet && mGuiShower)
		{
			std::vector<IAttrib*> attribs;
			mAttribSet->getAttribs(attribs);

			CEGUI::UVector2 size(mGuiShower->getSize().d_x, CEGUI::UDim(0.0f, 30.0f));
			CEGUI::UVector2 pos(CEGUI::UDim(0.0f, 0.0f), CEGUI::UDim(0.0f, 0.0f));
			for (size_t i=0; i<attribs.size(); ++i)
			{
				AttribSheet* sheet = new AttribSheet(attribs[i], mGuiShower);
				sheet->create();
				sheet->setGeomentry(pos, size);
				mAttribSheets.insert(std::make_pair(attribs[i]->getName(), sheet));

				pos.d_y.d_offset += size.d_y.d_offset + 2.0f;
			}
		}
	}
	void AttribSetShower::update()
	{
		for (std::map<String, AttribSheet*>::iterator it = mAttribSheets.begin();
			it != mAttribSheets.end(); ++it)
		{
			it->second->updateAttrib2Ui();
		}
	}
	void AttribSetShower::update(const String& attribName)
	{
		std::map<String, AttribSheet*>::iterator it = mAttribSheets.find(attribName);
		if (it != mAttribSheets.end())
		{
			it->second->updateAttrib2Ui();
		}
	}
	void AttribSetShower::clearUi()
	{
		for (std::map<String, AttribSheet*>::iterator it = mAttribSheets.begin();
			it != mAttribSheets.end(); ++it)
		{
			it->second->destroy();
			delete it->second;
		}
		mAttribSheets.clear();
	}
	void AttribSetShower::clearData()
	{
		clearUi();
		if (mAttribSet)
		{
			mAttribSet->destroy();
			delete mAttribSet;
			mAttribSet = 0;
		}
	}



}//iMate